package org.gten.gegedexin.IMAP;

import java.io.*;
import java.net.Socket;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

//除了附件，所有收取的消息都要解码

/**
 * 基于Socket实现IMAP协议
 * 该类只提供与邮件服务器建立Socket连接的服务，
 * 其余功能要在IMAPClient中实现
 * */
public class IMAP {
    private Socket socket = null;
    private boolean debug = false;
    private BufferedReader in;
    private BufferedWriter out;

    private int tag;//命令的序号
    private final String SPACE = " ";

    /**主函数*/
    public static void main(String[] args) throws IOException {
//        launchReceiveNewMail();
        testDecode();
    }

    /**构造函数*/
    public IMAP(String server, int port) {
        try {
            socket = new Socket(server, port);
            InputStreamReader inputStreamReader = new InputStreamReader(socket.getInputStream());
            in = new BufferedReader(inputStreamReader);
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());
            out = new BufferedWriter(outputStreamWriter);
            tag = 0;
            //服务器的响应与控制台输出相同
            String hello = in.readLine();//建立连接，读取服务器的第一条响应
            //e.g.* OK [CAPABILITY IMAP4 IMAP4rev1 ID AUTH=PLAIN AUTH=LOGIN AUTH=XOAUTH2 NAMESPACE] QQMail XMIMAP4Server ready
            if (!hello.startsWith("* OK")) {
                System.out.println("调用构造函数，服务器无应答！");
                System.exit(2);
            }
        } catch (Exception e) {
            System.out.println("调用构造函数，建立连接失败！");
            e.printStackTrace();
            System.exit(2);
        }
    }

    /**给文件管理系统提供的接口，默认收取收件箱里所有新邮件，
     * String folder
     * String destDir
     * @param user 用户名
     * @param password 密码，邮箱的激活码
     * @param folder 登录邮箱后，选择的文件夹
     * @param destDir 下载附件的目标目录
     * @return 如果邮箱中没有新邮件，那么返回null，
     * 如果有新邮件，那么按照顺序返回邮件的正文和主题，封装在MailDigest中
     * 返回所有邮件的正文和主题MailDigest的数组，附件下载到本地*/
    public ArrayList<MailDigest> receiveNewMail(String user, String password, String folder, String destDir) {
        ArrayList<MailDigest> mailDigests = null;
        try {
            login(user, password);
            select(folder);
            //获取所有新邮件的邮件号
            ArrayList<Integer> newMailNums = searchUnseenEmail();//获取新邮件的邮件号，如果没有新邮件newMailNums = null
            if (newMailNums == null)
                return null;//如果没有新邮件，直接返回null

            //读取新邮件，返回新邮件的摘要信息
            mailDigests = new ArrayList<>();
            int mailNum;
            String body, subject;
            for (int i = 0; i < newMailNums.size(); ++i) {
                mailNum = newMailNums.get(i);
                body = fetchEmail(mailNum, destDir);//读取邮件正文，下载附件
                subject = fetchSubject(mailNum);
                MailDigest mailDigest = new MailDigest(body, subject, null);
                mailDigests.add(mailDigest);
            }

            logout();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return mailDigests;
    }



    /**读取服务器返回的一行响应，
     * 在调试过程中，所有读取服务器一行响应的操作都应该由它完成
     * 如果服务器的响应是多行的，那么说明需要对返回的信息进行额外处理，
     * 该任务不应该交给getReturn()*/
    public String getReturn() {
        String line = "";
        try {
            line = in.readLine();
            if(debug) {
                System.out.println("S:" + line);//打印服务器的返回消息
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return line;
    }

    /**获得IMAP服务器响应的状态，如OK,NO,BAD等*/
    public String getResult(String response) {
        StringTokenizer st = new StringTokenizer(response, " ");//把服务器的响应划分为Token序列，用空格分割
        //因为在IMAP中每个命令开头添加了标识命令的序号，所以获取响应的第2个token
        st.nextToken();
        return st.nextToken();
    }

    /**
     * @param cmd 用户发送的命令
     * 向服务器发送命令
     * */
    private void sendServer(String cmd) throws IOException {
        out.write("A" + tag + " " + cmd);
        out.newLine();
        out.flush();

        if (debug) {
            System.out.println("C:" + "A" + tag + " " + cmd);
        }
        ++tag;//更新命令序号
    }

    /**LOGIN命令*/
    public void login(String user, String password) throws IOException {
        //a001 login 2424654364@qq.com ashbwgbuwikgdhhd
        sendServer("LOGIN " + user + " " + password);
        String result = getResult(getReturn());
        if (!"OK".equals(result)) {
            throw new IOException("用户名错误或密码错误！");
        }
    }

    /**SELECT命令*/
    public void select(String folder) throws IOException {
        sendServer("SELECT " + folder);
        String line;
        while ((line = getReturn()).startsWith("*")) {
            //do nothing
        }
        line = getResult(line);
        if (!"OK".equals(line)) {
            System.out.println("邮箱名字错误，请重新输入！");
        }
    }

    /**FETCH命令
     * @param mailNum 读取的邮件号
     * @param param fetch命令的附加参数
     * 提供简单的FETCH服务，只发送命令，不处理服务的响应
     */
    public void fetch(int mailNum, String param) throws IOException {
        sendServer("FETCH " + mailNum + SPACE + param);
    }

    //尝试Fetch邮件的主题，并使用Base64解码，看看是否可以获得可读的内容
    public void testFetchSubject() throws IOException {
        String firstSubject = fetchSubject(14);
        String secondSubject = fetchSubject(15);
        System.out.println("主题：");
        System.out.println("第一封邮件：" + firstSubject);
        System.out.println("第二封邮件：" + secondSubject);
    }

    /**
     * @param mailNum 邮件号
     * 进入文件夹后读取mailNum指定邮件的主题并返回
     * 尚未调试*/
    public String fetchSubject(int mailNum) throws IOException {
        fetch(mailNum, "BODY[HEADER.FIELDS (SUBJECT)]");
        String line, subject;
//        while (!(line = in.readLine()).startsWith("Subject"));
        while (!(line = in.readLine()).startsWith("subject"));//古惠思版本的主题，使用了全小写的subject
        line = line.substring(8).trim();
        /*if (line.startsWith("=?")) {//使用了MIME编码  注释的内容针对QQ邮箱
            StringTokenizer st = new StringTokenizer(line, "?");
            st.nextToken();//忽略第1个
            String charset = st.nextToken();
            String encoding = st.nextToken();
            String content = st.nextToken();
            subject =  decode(charset, encoding, content);
        }
        else
            subject = line;*/

        subject = decode("gbk", "base64", line);
        while (!(line = in.readLine()).startsWith("A")) {
            //do nothing
        }
        String result = getResult(line);
        if (!"OK".equals(result)) {
            System.out.println("调用fetchSubject错误，服务器返回：" + result);
        }

        return subject;
    }

    /**判断字符串内容是否使用了Quoted-Printable编码方式
     * @param str 字符串*/
    public static boolean isQuotedPrintable(String str) {
        if (str.startsWith("=?") && str.endsWith("?=") &&
                (str.split("\\?").length == 5)) {
            return true;
        }

        return false;
    }

    /**
     * Quoted-Printable解码*/
    public static byte[] decodeQuotedPrintable(byte[] bytes) throws Exception {
        if (bytes == null) {
            return null;
        }
        byte ESCAPE_CHAR = '=';
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        for (int i = 0; i < bytes.length; i++) {
            int b = bytes[i];
            if (b == ESCAPE_CHAR) {
                try {
                    int u = Character.digit((char) bytes[++i], 16);
                    int l = Character.digit((char) bytes[++i], 16);
                    if (u == -1 || l == -1) {
                        throw new Exception("Invalid quoted-printable encoding");
                    }
                    buffer.write((char) ((u << 4) + l));
                } catch (Exception e) {
                    throw new Exception("Invalid quoted-printable encoding");
                }
            } else {
                buffer.write(b);
            }
        }
        return buffer.toByteArray();
    }

    /**
     * @param charset 字符集
     * @param encoding  编码方式，B:Base64, Q:Quoted-Printable
     * @param content 需要解码的内容
     * 输入一个编码的字符串，返回一个使用目标字符集的可读的字符串*/
    public static String decode(String charset, String encoding, String content)
            throws UnsupportedEncodingException {
        if ("7bit".equals(encoding) || "8bit".equals(encoding)
                || "binary".equals(encoding)) {//编码方式为"7bit", "8bit"或者"binary", 不用解码
            return content;
        }
        else if ("B".equals(encoding) || "base64".equals(encoding)) {//base64编码
            byte[] decodedBytes = Base64.getDecoder().decode(content);
            return new String(decodedBytes, charset);
        }
        else if ("Q".equals(encoding)) {//Quoted-Printable编码
            try {
                byte[] decodedBytes = decodeQuotedPrintable(content.getBytes());
                return new String(decodedBytes, charset);
            } catch (Exception ex) {
                System.out.println("调用decodeQuotedPrintable时出错，QP编码错误");
                return null;
            }
        }
        else {
            System.out.println("出现错误的编码方式：" + encoding);
            return null;
        }
    }

    public static void testDecode() throws UnsupportedEncodingException {
        String line = "xOO6ww1nb29kYnllDaSipOqkrKTIpKY=";
        byte[] bytes = line.getBytes("gbk");
//        String line = "5aW9";
//        byte[] decodedSubject = Base64.getDecoder().decode(line);
//        System.out.println("解码后的主题是：\n" + new String(decodedSubject, "gb18030"));//nice，可以解码了
        //=?gb18030?B?tPi4vbz+tcQ=?=
        //"?"用作分隔符，gb18030是charset在206行生成String的时候使用，"B"代表使用Base64编码，"tPi4vbz+tcQ"是邮件主题的内容
        line = decode("GBK", "base64", line);
        System.out.print("解码后的内容\n" + line);
    }

    /**
     * 获取邮件的时间*/
    public Date fetchDate(int mailNum) throws IOException, ParseException {
        /*
        读取含有日期的行，处理该行，获取Date对象
        读取剩余行，获取命令状态
        * */
        fetch(mailNum, "BODY[HEADER.FIELDS (DATE)]");
        String line, result;
        while (!(line = in.readLine()).startsWith("Date")) {
            //do nothing
        }
        line = line.substring(6).trim();
        SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
        Date date = sdf.parse(line);

        while (!(line = in.readLine()).startsWith("A")) {
            //do nothing
        }
        result = getResult(line);
        if (!"OK".equals(result)) {
            System.out.println("调用fetchDate错误，服务器返回：" + result);
        }

        return date;
    }

    /**获取指定邮件的正文，固定使用base64编码，gbk字符集
     * 前提：已经选择了文件夹*/
    public String fetchBody(int mailNum) throws IOException {
        fetch(mailNum, "BODY[TEXT]");
        String body = null;
//        A3 FETCH 12 BODY[TEXT]
//        * 12 FETCH (BODY[TEXT] {42}
//        xOO6ww0KZ29vZGJ5ZQ0KpKKk6qSspMikpg==
//
//
//        )
//        A3 OK FETCH Completed
        String line = in.readLine();
        if (line.contains("not exist")) {
            System.out.println("错误，邮箱中不存在" + mailNum + "号邮件");
            return body;
        }
        if  (!line.startsWith("*")) {
            System.out.println("邮箱中存在" + mailNum + "号邮件，但是命令错误");
            return body;
        }
        StringBuilder stringBuilder = new StringBuilder();
        while (!(line = in.readLine()).startsWith(")")) {
            stringBuilder.append(line);
        }
        String result = getResult(getReturn());
        if  (!"OK".equals(result)) {
            System.out.println("命令FETCH mailNum BODY[TEXT]错误");
            return body;//返回null
        }
        body = stringBuilder.toString().trim();
        body = decode("gbk", "base64", body);

        return body;
    }

    /**下载邮件的附件到本地
     * @param bodyIndex 邮件体中附件的下标,A3 fetch 4 BODY[bodyIndex]
     * */
    public void fetchAppendix(int mailNum, int bodyIndex, String destDir, String fileName) throws IOException {
        //接收附件然后存储在本地
        //向服务器发送fetch BODY[BODYINDEX]命令，然后根据文件名把文件下载到指定目录下
        //发送命令，处理相应，状态，读取多余响应，返回
        fetch(mailNum, "BODY[" + bodyIndex + "]");
        //服务器发送的响应是字节序列，但是为了在客户端显示，所以使用了binary-to-text编码，比如base64
        //读取到字符串后，把它编码获取到byte序列，然后结合文件操作，就能够下载附件
        String line = getReturn();
        if (line.contains("NIL")) {
            System.out.println("调用fetchAppendix，FETCH mailNum BODY[bodyIndex]，BODY[" + bodyIndex + "]为空！");
            return;
        }
        //        while (!(line = in.readLine()).endsWith(")")) {//经过base64编码的附件内容不含有")"，并且附件内容以")"结尾
//            stringBuilder.append(line.trim());//读入一行，要去除换行符
//        }
        //古惠思版本的附件，其内容只有一行
        String appendixContent = in.readLine().trim();
        String result = getResult(getReturn());
        if (!"OK".equals(result)) {
            System.out.println("调用fetchAppendix，读取附件时出错！");
            return;
        }

        //解码读取到的数据，存储到本地
        appendixContent = appendixContent.substring(0, appendixContent.length() - 1);//去掉最后一个")"
        byte[] bytes = Base64.getDecoder().decode(appendixContent);//附件一般采用base64编码，但有可能变化
        downloadFile(bytes, destDir, fileName);
    }

    /**下载文件
     * @param bytes 解码后的byte序列，因为假设编码方式都为base64，所以暂且如此处理
     * @param destDir 目标目录
     * @param fileName 文件名，根据它来决定文件解码方式
     *                 IMAP对于.txt文件会应用base64编码
     * */
    public static void downloadFile(byte[] bytes, String destDir, String fileName) throws IOException {
        File dest = new File(destDir + fileName);//创建文件对象
        FileOutputStream output = new FileOutputStream(dest);//创建文件输出流
        output.write(bytes, 0, bytes.length);
        output.close();
    }

    /**获取一封完整的email，返回正文部分，所有附件下载到本地
     * @param mailNum 邮件号
     * @return 邮件正文部分
     * */
    public String fetchEmail(int mailNum, String destDir) throws IOException {
        //发送命令，处理相应，状态，读取多余响应，返回
        fetch(mailNum, "BODYSTRUCTURE");//确定附件个数，获取每一个附件的"filename"
        //e.g.响应只有两行
//        * 5 FETCH (BODYSTRUCTURE ((("TEXT" "PLAIN" ("charset" "GBK") NIL NIL "BASE64" 66 1 NIL NIL NIL)("TEXT" "HTML" ("charset" "GBK") NIL NIL "BASE64" 264 4 NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "----=_Part_181529_1033436252.1686644932826") NIL NIL)("APPLICATION" "PDF" ("name" "=?gbk?B?Y2gxLnBkZg==?=") NIL NIL "BASE64" 7299456 NIL ("attachment" ("filename" "ch1.pdf")) NIL)("APPLICATION" "PDF" ("name" "=?gbk?B?Y2gyYS5wZGY=?=") NIL NIL "BASE64" 3477132 NIL ("attachment" ("filename" "ch2a.pdf")) NIL) "MIXED" ("BOUNDARY" "----=_Part_181527_383555183.1686644932826") NIL NIL))
//        A5 OK FETCH Completed
        String line = in.readLine();//临时变量line，从输入读取的一行
        if (line.contains("not exist")) {//访问了邮箱中不存在的邮件
            System.out.println("调用fetchBody，邮箱中不存在" + mailNum + "号邮件");
            return null;
        }
        //计算附件的个数，并获取附件的文件名
        StringTokenizer st = new StringTokenizer(line, "\"");//使用"作为分隔符
        String token;
        int appendixNum = 0;//计算附件个数
        ArrayList<String> appendixFileNames = new ArrayList<>();//存储附件名称的数组
        while (st.hasMoreTokens()) {
//            ("attachment" ("filename" "ch1.pdf"))
//            "APPLICATION" "OCTET-STREAM"
            token = st.nextToken();
            if ("attachment".equals(token)) {//计算附件数量的方式需要修改
                ++appendixNum;
                st.nextToken();
                token = st.nextToken();
                if (!"filename".equals(token)) {
                    System.out.println("调用fetchEmail，解析附件文件名时出错！");
                    return null;
                }
                st.nextToken();
                String fileName = st.nextToken();//此时，token为文件名
                //注意Token有可能是Q编码的形式，要加以处理
//                ("attachment" ("filename" "=?GBK?Q?=D0=A1=B8=BD=BC=FE.pdf?=")) NIL)
//                The form is: "=?charset?encoding?encoded text?=".被"?"分割成5个部分
                if (isQuotedPrintable(fileName)) {//如果文件名使用Q编码
                    //获取charset和content
                    String[] substrings = fileName.split("\\?");
                    String charset = substrings[1];
                    String content = substrings[3];
                    fileName = decode(charset, "Q", content);
                }
                appendixFileNames.add(fileName);
            }
        }

//        String replacedLine = line.replace("\"attachment\"", "");
//        appendixNum = (line.length() - replacedLine.length()) / 10;
        String result = getResult(getReturn());//获取命令状态
        if (!"OK".equals(result)) {
            System.out.println("调用fetchEmail,获取BODYSTRUCTURE时出错！");
        }

        //读取邮件正文
        //如果邮件不含附件，调用BODY[TEXT]
        //含有附件，调用BODY[1]
        if (appendixNum > 0)
            fetch(mailNum, "BODY[1]");
        else
            fetch(mailNum, "BODY[TEXT]");
        String body;//邮件的正文部分
        line = in.readLine();
        if (!line.startsWith("*")) {//出现异常，退出
            System.out.println("调用fetchBody，邮箱中存在" + mailNum +
                    "号邮件，但是出现了其他异常");
            return null;
        }

        //正常读取
        while (!(line = in.readLine()).startsWith("Content-Type")) {//循环读取，直至达到Content-Type
            //do nothing
        }
        //获取字符集
        if (!line.contains("charset"))//如果邮件不含有附件，charset在Content-Type的下一行，那么读取额外的一行
            line = in.readLine();
        int indexOfCharset = line.indexOf("charset=");//找到charset的开始位置
        line = line.substring(indexOfCharset + 8).trim();
        st = new StringTokenizer(line, "\"");//使用"分割字符集
        String charset = st.nextToken();
        while (!(line = in.readLine()).startsWith("Content-Transfer-Encoding"));//do thing
        String encoding = line.substring("Content-Transfer-Encoding:".length()).trim();
//        if (debug) {
//            System.out.println("字符集" + charset);
//            System.out.println("编码" + encoding);
//        }

        StringBuilder stringBuilder = new StringBuilder();
        while (!(line = in.readLine()).startsWith("------=_")) {
            //循环读入邮件正文内容，直至遇到分隔符，要非常注意分隔符的选取，
            // 因为不同服务器使用的分隔符不同，所以要特别小心潜在的错误
            stringBuilder.append(line);
        }
        //获取邮件正文
        body = stringBuilder.toString().trim();
        body = decode(charset, encoding, body);
        //读取剩余响应
        while (!in.readLine().startsWith(")"));
        result = getResult(in.readLine());
        if (!"OK".equals(result)) {
            System.out.println("调用fetchEmail获取邮件正文时出错！");
        }


        //读取附件，从2开始计数，读取到的字符串假设都使用base64编码
        for (int i = 2; i < appendixNum + 2; ++i) {
            fetchAppendix(mailNum, i, destDir, appendixFileNames.get(i - 2));
        }
        return body;
    }

    /*
     * 首先利用search 查找未来读邮件。使用bodystructure确定附件个数。
     * 读取正文。附件保存到本地
     * */

    /**获取邮件的内容类型，会返回charset (if any)*/
    public String fetchContentType(int mailNum) throws IOException {
        //e.g.
//        Content-Type:application/octet-stream
//        Content-Type: text/plain; charset=utf-8
        fetch(mailNum, "BODY[HEADER.FIELDS (Content-Type)]");
        String line = in.readLine();
        if (line.contains("not exist")) {
            System.out.println("错误，邮箱中不存在" + mailNum + "号邮件");
            return null;
        }

        while (!(line = getReturn()).startsWith("Content-Type"));
        String contentType = line.substring("Content-Type:".length()).trim();
        while (!in.readLine().startsWith(")"));
        String result = getResult(getReturn());
        if (!"OK".equals(result)) {
            System.out.println("命令FETCH mailNum BODY[HEADER.FIELDS (Content-Type)]错误");
            return null;
        }

        return contentType;
    }

    /**获取邮件的编码方式*/
    public String fetchEcoding(int mailNum) throws IOException {
        fetch(mailNum, "BODY[HEADER.FIELDS (Content-Transfer-Encoding)]");
        String line = in.readLine();
        if (line.contains("not exit")) {
            System.out.println("错误，邮箱中不存在" + mailNum + "号邮件");
            return null;
        }

        while (!(line = in.readLine()).startsWith("Content-Transfer-Encoding"));
        String encoding = line.substring("Content-Transfer-Encoding:".length()).trim();
        while (!in.readLine().startsWith(")"));
        String result = getResult(getReturn());
        if (!"OK".equals(result)) {
            System.out.println("命令FETCH mailNum BODY[HEADER.FIELDS (Content-Transfer-Encoding)]错误");
            return null;
        }

        return encoding;
    }

    public String fetchContentDeposition(int mailNum) throws IOException {
        fetch(mailNum, "BODY[HEADER.FIELDS (Content-Disposition)]");
        String line = in.readLine();
        if (line.contains("not exist")) {
            System.out.println("错误，邮箱中不存在" + mailNum + "号邮件");
            return null;
        }

        while (!(line = in.readLine()).startsWith("Content-Disposition"));
        String contentDeposition = line.substring("Content-Disposition:".length()).trim();
        while (!in.readLine().startsWith(")"));
        String result = getResult(getReturn());
        if (!"OK".equals(result)) {
            System.out.println("命令FETCH mailNum BODY[HEADER.FIELDS (Content-Deposition)]错误");
            return null;
        }

        return contentDeposition;
    }

    /*
     * 获取正文时，使用FETCH <mailNum> BODY[TEXT]命令，可以获取正文的编码方式
     * 获取附件时，使用FETCH <mailNum> BODY[2]命令，正文内容使用Base64编码
     * IMAP类只包含最基础的功能，然后用一个IMAPClient类，来封装基于IMAP实现的功能
     * */
    /**读取mailNum指定的一封邮件，判断是否含有附件，
     * 如果含有附件，那么使用附件的编码方式，下载附件。
     * 如果不含有附件，那么读取它的主题和正文。
     * 读取的结果都封装在MailDigest中*/
    public MailDigest fetchSingleAtmtEmail(int mailNum, String destDir) throws IOException {
        //fetch <mialNum> body[header]
        //通过头部获取附件的编码方式，邮件名等
        //通过对比有无附件的邮件的区别，决定判断邮件数量的方式：1.头部信息
        //获取邮件正文时，需要考虑正文为空的情况
        //确定附件数量: 0或1
        //确定正文编码方式
        //确定附件编码方式
        MailDigest mailDigest;
        String subject = fetchSubject(mailNum);
        String body = null;
        File appendix = null;
        String contentType = fetchContentType(mailNum);
        if (contentType.contains("text/plain")) {//只含有正文
            body = fetchBody(mailNum);
            if (body == null) {
                System.out.println("读取邮件正文时出错，body为null");
                return null;
            }
        }
        else {//只含有附件，附件采用base64编码
            String contentDeposit = fetchContentDeposition(mailNum);
            String[] substrings = contentDeposit.split("\"");
            String fileName = subject + substrings[1];//根据邮件主题和文件名来下载附件，避免附件名字冲突
            fetchAppendix(mailNum, 2, destDir, fileName);//把附件下载到本地
            appendix = new File(destDir + fileName);
        }

        mailDigest = new MailDigest(body, subject, appendix);
        //读取结束后，需要更新邮件夹中指定邮件的状态，改为已读
        seenEmail(mailNum);

        return mailDigest;
    }

    /**store命令，更改邮件的属性
     * 前提：已经选择了一个文件夹
     * */
    public void store(int mailNum, String param) throws IOException {
        //A5 STORE 10 +FLAGS (\SEEN)
        //A5 NO Mails not exist!
        //A4 STORE 5 +FLAGS (\SEEN)
        //* 5 FETCH (FLAGS (\Seen))
        //A4 OK STORE Completed
        sendServer("STORE " + mailNum + SPACE + param);
        String line = in.readLine();
        if (line.contains("not exist")) {
            System.out.println("错误，邮箱中不存在" + mailNum + "号邮件");
            return;
        }
        if (!line.startsWith("*")) {
            System.out.println("邮件存在，但是命令错误");
            return;
        }
        String result = getResult(getReturn());
        if (!"OK".equals(result)) {
            System.out.println("命令STORE mailNum PARAM错误");
        }
    }

    /**把邮件标记为已读
     * 前提：已经选择了文件夹*/
    public void seenEmail(int mailNum) throws IOException {
        store(mailNum, "+FLAGS (\\SEEN)");
    }

    /**把文件标记为未读
     * 前提：已经选择了文件夹*/
    public void unseenEmail(int mailNum) throws IOException {
        store(mailNum, "-FLAGS (\\SEEN)");
    }

    /**search命令，仅向服务器发送命令，do nothing
     * @param param search命令的参数，这是必须的，否则无法使用*/
    public void search(String param) throws IOException {
        sendServer("SEARCH " + param);
    }

    /**查找指定文件夹的新邮件，并返回未读邮件的邮件号
     * @return 未读邮件的邮件编号数组，如果邮箱中没有未读邮件，那么返回null*/
    public ArrayList<Integer> searchUnseenEmail() throws IOException {
        //发送命令，处理相应，状态，读取多余响应，返回
//        * SEARCH 2 4
//        A6 OK SEARCH completed
        search("UNSEEN");//只返回两行消息
        ArrayList<Integer> mailNumSeq = new ArrayList<>();

        String line = getReturn();
        StringTokenizer st = new StringTokenizer(line, " ");//使用空格分割响应
        st.nextToken();
        st.nextToken();//丢弃两个token

        int mailNum;
        while (st.hasMoreTokens()) {
            //读取邮件号，转换为整数，添加到数组中
            mailNum = Integer.parseInt(st.nextToken());
            mailNumSeq.add(mailNum);
        }

        line = getResult(getReturn());
        if (!"OK".equals(line)) {
            System.out.println("调用searchUnseenEmail，读取邮箱中新邮件时发生错误！");
        }

        if (mailNumSeq.size() == 0)
            return null;

        return mailNumSeq;
    }

    /**LOGOUT命令*/
    public void logout() throws IOException {
//        a010 LOGOUT
//        S:* BYE LOGOUT received
//        S:a010 OK LOGOUT Completed
        sendServer("LOGOUT");
        getReturn();//读取服务器的一行响应并丢弃
        String result = getResult(getReturn());

        if (!"OK".equals(result)) {
            System.out.println("服务器回应：" + result);
            throw new IOException("未能正确退出！");
        }

        in.close();
        out.close();
        socket.close();
    }

    /**receiveNewMail的启动函数，给后端提供傻瓜式接口*/
    public static ArrayList<MailDigest> launchReceiveNewMail() throws IOException {
        String server = "imap.qq.com";
        String user = "2424654364@qq.com";
        String password = "ashbwgbuwikgdhhd";
        int port = 143;//imap服务器默认端口为143，如果不可用，换为993
        String folder = "INBOX";//默认邮箱文件夹和附件下载路径
        String destDir = "D:\\0软件实训\\appendix\\";
        IMAP imapClient = new IMAP(server, port);
//        构造IMAPClient，建立连接后查看服务器的响应
        ArrayList<MailDigest> mailDigests;
        mailDigests = imapClient.receiveNewMail(user, password, folder, destDir);
        return mailDigests;
    }

    /**专门用于测试的函数*/
    public static void test() throws UnsupportedEncodingException {
       /*
       *         charset="gb18030"
        Content-Transfer-Encoding: base64*/
        String charset = "gb18030";
        String encoding = "B";
        String content;
        content = "08q8/tX9zsQNCiZuYnNwOw==";
//       System.out.println("第1封邮件正文内容：" + decode(charset, encoding, content));
//       content = "2Om2+yDFrtfTDQoNCg0KRGFuZGVsaW9uDQoyNTM1OTQzMDE4QHFxLmNvbQ0KDQoNCg0KJm5ic3A7";

//       System.out.println("正文内容：" + decode(charset, encoding, content));
//       content = "MTIzDQpzZGZnZGYNCmdoaw0KcnR5";//附件内容
//       content = "suLK1Leiy81wZGa4vbz+o6yy2df3z7XNs7Xa0rvVwqGj";//163
//       content = "2Om2+yDFrtfTDQoNCg0KRGFuZGVsaW9uDQoyNTM1OTQzMDE4QHFxLmNvbQ0KDQoNCg0KJm5i" + "c3A7";//qq邮箱
        System.out.println("邮件正文：\n" + decode(charset, encoding, content));
        //
    }

}
